<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>HTTP 代理原理和实现 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="HTTP 代理原理和实现 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2017/03/21/http-proxy-and-golang-implementation/">

	
	<meta property="article:published_time" content="2017-03-21T00:03:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2017/03/21/http-proxy-and-golang-implementation/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2017-03-20T16:00:00.000Z" itemprop="datePublished">
                    2017-03-21
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">HTTP 代理原理和实现</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <p>代理的核心功能可以用一句话概括：接受客户端的请求，转发到后端服务器，获得应答之后返回给客户端。下图是 《HTTP 权威指南》一书中给出的图例，可以很清晰地说明这一流程：</p>
<p><img src="https://st.imququ.com/i/webp/static/uploads/2015/11/web_proxy.png.webp" alt=""></p>
<p>代理的功能有很多，事实上整个互联网到处都充斥着代理服务器。如果所有的 HTTP 访问都是客户端和服务器端直接进行的话，我们的网络不仅会变得缓慢，而且性能会大打折扣。</p>
<p>代理服务器根据不同的配置和使用，可能会有不同的功能，这些功能主要包括：</p>
<ul>
<li>内容过滤：代理可以根据一定的规则限制某些请求的连接。比如有些公司会设置内部网络无法访问某些购物、游戏网站，或者学校的网络不让学生访问色情暴力的网站等</li>
<li>节省成本：代理服务器可以作为缓存使用，对于某些资源只需要第一次访问的时候去下载，以后代理直接把缓存的结果返回给客户端，节约网络带宽的开销</li>
<li>提高性能：通过代理服务器的缓存（比如 CDN）和负载均衡（比如 nginx lb）功能，服务器端可以加速请求的访问，在更快的时间内返回结果）</li>
<li>增加安全性：公司可以在内网和外网之间通过代理进行转发，这样不仅对外隐藏了实现的细节，而且可以在代理层对爬虫、病毒性请求进行过滤，保护内部服务</li>
</ul>
<p>所有的这些功能的实现都依赖于代理的特性，它可以在客户端和服务器端做一些事情，根据代理做的事情不同，它的角色和功能也就不同。那么，代理具体可以做哪些事情呢？比如：</p>
<ul>
<li>修改 HTTP 请求：url、header、body</li>
<li>过滤请求：根据一定的规则丢弃、过滤请求</li>
<li>决定转发到哪个后端（可以是静态定义的，也可以是动态决定）</li>
<li>保存服务器的应答，后续的请求可以直接使用保存的应答</li>
<li>修改应答：对应答做一些格式的转换，修改数据，甚至返回完全不一样的应答数据</li>
<li>重试机制，如果后端服务器暂时无法响应，隔一段时间重试</li>
<li>……</li>
</ul>
<h2 id="正向代理和反向代理"><a href="#正向代理和反向代理" class="headerlink" title="正向代理和反向代理"></a>正向代理和反向代理</h2><p>代理可以分为正向代理和反向代理两种。</p>
<p>正向代理需要客户端来配置，一般来说我们会通过浏览器或者操作系统提供的工具或者界面来配置。这个时候，代理对客户端不是透明的，客户端需要知道代理的地址并且手动配置。配置了代理，浏览器在发送请求的时候会对报文做特殊的修改。</p>
<p>反向代理对客户端是透明的，也就是说客户端一般不知道代理的存在，认为自己是直接和服务器通信。我们大部分访问的网站就是反向代理服务器，反向代理服务器会转发到真正的服务器，一般在反向代理这一层实现负载均衡和高可用的功能。而且这里也可以看到，客户端是不会知道真正服务器端的 ip 地址和端口的，这在一定程度上起到了安全保护的作用。</p>
<h3 id="代理服务器怎么知道目的服务器的地址？"><a href="#代理服务器怎么知道目的服务器的地址？" class="headerlink" title="代理服务器怎么知道目的服务器的地址？"></a>代理服务器怎么知道目的服务器的地址？</h3><p>在反向代理中，代理服务器要转发的服务器地址都是事先知道的（包括静态配置和动态配置）。比如<a href="http://nginx.org/en/docs/http/load_balancing.html" target="_blank" rel="noopener">使用 nginx 来配置负载均衡</a>。</p>
<p>而对于正向代理来说，客户端可能访问的服务器地址是无法事先知道的。因为HTTP 协议活动在应用层，它无法获取网络层（IP层）信息，那么该协议要有一个地方可以拿到这个信息。HTTP 中可能保存这个信息的地方有两个：URL 和 header。默认情况下，HTTP 请求的 status line 有三部分组成：方法、uri 和协议版本，比如：</p>
<pre><code>GET /index.html HTTP/1.0
User-Agent: gohttp 1.0
</code></pre><p>如果客户端（比如浏览器）知道自己在通过正向代理进行报文传输，那么它会在 status line 加上要访问服务器的真实地址。这个时候发送的报文是：</p>
<pre><code>GET http://www.marys-antiques.com/index.html HTTP/1.0
User-Agent: gohttp 1.0
</code></pre><h3 id="代理路径"><a href="#代理路径" class="headerlink" title="代理路径"></a>代理路径</h3><p>客户端不管是通过代理服务器，还是直接访问后端服务器对于最终的结果是没有区别的，也就是说大多数情况下客户端根本不关心它访问的到底是什么，只需要（准确快速地）拿到想要的信息就够了。但是有时候，我们还是希望知道请求到底在中间经历了哪些代理，比如用来调试网络异常，或者做数据统计，而 HTTP 协议也提供了响应的功能。</p>
<p>虽然 RFC 2616 定义了 <code>Via</code> 头部字段来跟踪 HTTP 请求经过的代理路径，但在实际中用的更多的还是 <a href="https://en.wikipedia.org/wiki/X-Forwarded-For" target="_blank" rel="noopener"><code>X-Forwarded-For</code></a> 字段，<code>X-Forwarded-For</code> 是 <code>Squid</code> 缓存代理服务软件引入的，目前已经在规范化在 <a href="https://tools.ietf.org/html/rfc7239" target="_blank" rel="noopener">RFC 7239</a> 文档。</p>
<p><code>X-Forwarded-For</code> 头部格式也比较简单，比如某个服务器接受到请求的对应头部可能是：</p>
<pre class=" language-bash"><code class="language-bash">X-Forwarded-For: client, proxy1, proxy2
</code></pre>
<p>对应的值有多个字段，每个字段代表中间的一个节点，它们之间由逗号和空格隔开，从左到右距离当前节点越来越近。</p>
<p>每个代理服务器会在 <code>X-Forwarded-For</code> 头部填上前一个节点的 ip 地址，这个地址可以通过 TCP 请求的 remote address 获取。为什么每个代理服务器不填写自己的 ip 地址呢？有两个原因，如果由代理服务器填写自己的 ip 地址，那么代理可以很简单地伪造这个地址，而上一个节点的 remote address 是根据 TCP 连接获取的（如果不建立正确的 TCP 连接是无法进行 HTTP 通信的）；另外一个原因是如果由当前节点填写 <code>X-Forwarded-For</code>，那么很多情况客户端无法判断自己是否会通过代理的。</p>
<p><strong>NOTE</strong>：</p>
<ol>
<li>最终客户端或者服务器端接受的请求，<code>X-Forwarded-For</code> 是没有最邻近节点的 ip 地址的，而这个地址可以通过 remote address 获取</li>
<li>每个节点（不管是客户端、代理服务器、真实服务器）都可以随便更改 <code>X-Forwarded-For</code> 的值，因此这个字段只能作为参考</li>
</ol>
<h2 id="代理服务器实现"><a href="#代理服务器实现" class="headerlink" title="代理服务器实现"></a>代理服务器实现</h2><p>这个部分我们会介绍如何用 golang 来实现 HTTP 代理服务器，需要读者了解一些 HTTP 服务器端编程的知识，可以参考我之前的文章：<a href="http://cizixs.com/2016/08/17/golang-http-server-side">go http 服务器编程</a>。</p>
<h3 id="正向代理"><a href="#正向代理" class="headerlink" title="正向代理"></a>正向代理</h3><p>按照我们之前介绍的代理原理，我们可以编写出这样的代码：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"fmt"</span>
    <span class="token string">"io"</span>
    <span class="token string">"net"</span>
    <span class="token string">"net/http"</span>
    <span class="token string">"strings"</span>
<span class="token punctuation">)</span>

<span class="token keyword">type</span> Pxy <span class="token keyword">struct</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>Pxy<span class="token punctuation">)</span> <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>rw http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> req <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Received request %s %s %s\n"</span><span class="token punctuation">,</span> req<span class="token punctuation">.</span>Method<span class="token punctuation">,</span> req<span class="token punctuation">.</span>Host<span class="token punctuation">,</span> req<span class="token punctuation">.</span>RemoteAddr<span class="token punctuation">)</span>

    transport <span class="token operator">:=</span>  http<span class="token punctuation">.</span>DefaultTransport

    <span class="token comment" spellcheck="true">// step 1</span>
    outReq <span class="token operator">:=</span> <span class="token function">new</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span>
    <span class="token operator">*</span>outReq <span class="token operator">=</span> <span class="token operator">*</span>req <span class="token comment" spellcheck="true">// this only does shallow copies of maps</span>

    <span class="token keyword">if</span> clientIP<span class="token punctuation">,</span> <span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> net<span class="token punctuation">.</span><span class="token function">SplitHostPort</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>RemoteAddr<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> prior<span class="token punctuation">,</span> ok <span class="token operator">:=</span> outReq<span class="token punctuation">.</span>Header<span class="token punctuation">[</span><span class="token string">"X-Forwarded-For"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> ok <span class="token punctuation">{</span>
            clientIP <span class="token operator">=</span> strings<span class="token punctuation">.</span><span class="token function">Join</span><span class="token punctuation">(</span>prior<span class="token punctuation">,</span> <span class="token string">", "</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">", "</span> <span class="token operator">+</span> clientIP
        <span class="token punctuation">}</span>
        outReq<span class="token punctuation">.</span>Header<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"X-Forwarded-For"</span><span class="token punctuation">,</span> clientIP<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>

    <span class="token comment" spellcheck="true">// step 2</span>
    res<span class="token punctuation">,</span> err <span class="token operator">:=</span> transport<span class="token punctuation">.</span><span class="token function">RoundTrip</span><span class="token punctuation">(</span>outReq<span class="token punctuation">)</span>
    <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
        rw<span class="token punctuation">.</span><span class="token function">WriteHeader</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusBadGateway<span class="token punctuation">)</span>
        <span class="token keyword">return</span>
    <span class="token punctuation">}</span>

    <span class="token comment" spellcheck="true">// step 3</span>
    <span class="token keyword">for</span> key<span class="token punctuation">,</span> value <span class="token operator">:=</span> <span class="token keyword">range</span> res<span class="token punctuation">.</span>Header <span class="token punctuation">{</span>
        <span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> value <span class="token punctuation">{</span>
            rw<span class="token punctuation">.</span><span class="token function">Header</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> v<span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    rw<span class="token punctuation">.</span><span class="token function">WriteHeader</span><span class="token punctuation">(</span>res<span class="token punctuation">.</span>StatusCode<span class="token punctuation">)</span>
    io<span class="token punctuation">.</span><span class="token function">Copy</span><span class="token punctuation">(</span>rw<span class="token punctuation">,</span> res<span class="token punctuation">.</span>Body<span class="token punctuation">)</span>
    res<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Serve on :8080"</span><span class="token punctuation">)</span>
    http<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>Pxy<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
    http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">"0.0.0.0:8080"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>这段代码比较直观，只包含了最核心的代码逻辑，完全按照最上面的代理图例进行组织。一共分成几个步骤：</p>
<ol>
<li>代理接收到客户端的请求，复制了原来的请求对象，并根据数据配置新请求的各种参数（添加上 <code>X-Forward-For</code> 头部等）</li>
<li>把新请求发送到服务器端，并接收到服务器端返回的响应</li>
<li>代理服务器对响应做一些处理，然后返回给客户端</li>
</ol>
<p>上面的代码运行之后，会在本地的 <code>8080</code> 端口启动代理服务。修改浏览器的代理为 <code>127.0.0.1：:8080</code> 再访问网站，可以验证代理正常工作，也能看到它在终端打印出所有的请求信息。</p>
<p>虽然这段代码非常简短，但是你可以添加更多的逻辑实现非常有用的功能。比如在请求发送之前进行过滤，根据一定的规则直接阻止某些请求的访问；或者对请求进行限流，某个客户端在一定的时间里执行的请求有最大限额；统计请求的数据进行分析等等。</p>
<p>这个代理目前不支持 HTTPS 协议，因为它只提供了 HTTP 请求的转发功能，并没有处理证书和认证有关的内容。如果了解 HTTPS 协议的话，你会明白这种模式下是无法完成 HTTPS 握手的，虽然代理可以和真正的服务器建立连接（知道了对方的公钥和证书），但是代理无法代表服务器和客户端建立连接，因为代理服务器无法知道真正服务器的私钥。</p>
<h3 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a>反向代理</h3><p>编写反向代理按照上面的思路当然没有问题，只需要在第二步的时候，根据之前的配置修改 <code>outReq</code> 的 URL Host 地址可以了。不过 Golang 已经给我们提供了编写代理的框架：<a href="https://golang.org/pkg/net/http/httputil/#ReverseProxy" target="_blank" rel="noopener"><code>httputil.ReverseProxy</code></a>。我们可以用非常简短的代码来实现自己的代理，而且内部的细节问题都已经被很好地处理了。</p>
<p>这部分我们会实现一个简单的反向代理，它能够对请求实现负载均衡，随机地把请求发送给某些配置好的后端服务器。使用 <code>httputil.ReverseProxy</code> 编写反向代理最重要的就是实现自己的 <code>Director</code> 对象，这是 <code>GoDoc</code> 对它的介绍：</p>
<blockquote>
<p>Director must be a function which modifies the request into a new request to be sent using Transport.<br>Its response is then copied back to the original client unmodified.<br>Director must not access the provided Request after returning.</p>
</blockquote>
<p>简单翻译的话，<code>Director</code> 是一个函数，它接受一个请求作为参数，然后对其进行修改。修改后的请求会实际发送给服务器端，因此我们编写自己的 <code>Director</code> 函数，每次把请求的 Scheme 和 Host 修改成某个后端服务器的地址，就能实现负载均衡的效果（其实上面的正向代理也可以通过相同的方法实现）。看代码：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
        <span class="token string">"log"</span>
        <span class="token string">"math/rand"</span>
        <span class="token string">"net/http"</span>
        <span class="token string">"net/http/httputil"</span>
        <span class="token string">"net/url"</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">NewMultipleHostsReverseProxy</span><span class="token punctuation">(</span>targets <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">*</span>url<span class="token punctuation">.</span>URL<span class="token punctuation">)</span> <span class="token operator">*</span>httputil<span class="token punctuation">.</span>ReverseProxy <span class="token punctuation">{</span>
        director <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>req <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                target <span class="token operator">:=</span> targets<span class="token punctuation">[</span>rand<span class="token punctuation">.</span><span class="token function">Int</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">%</span><span class="token function">len</span><span class="token punctuation">(</span>targets<span class="token punctuation">)</span><span class="token punctuation">]</span>
                req<span class="token punctuation">.</span>URL<span class="token punctuation">.</span>Scheme <span class="token operator">=</span> target<span class="token punctuation">.</span>Scheme
                req<span class="token punctuation">.</span>URL<span class="token punctuation">.</span>Host <span class="token operator">=</span> target<span class="token punctuation">.</span>Host
                req<span class="token punctuation">.</span>URL<span class="token punctuation">.</span>Path <span class="token operator">=</span> target<span class="token punctuation">.</span>Path
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> <span class="token operator">&amp;</span>httputil<span class="token punctuation">.</span>ReverseProxy<span class="token punctuation">{</span>Director<span class="token punctuation">:</span> director<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        proxy <span class="token operator">:=</span> <span class="token function">NewMultipleHostsReverseProxy</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">*</span>url<span class="token punctuation">.</span>URL<span class="token punctuation">{</span>
                <span class="token punctuation">{</span>
                        Scheme<span class="token punctuation">:</span> <span class="token string">"http"</span><span class="token punctuation">,</span>
                        Host<span class="token punctuation">:</span>   <span class="token string">"localhost:9091"</span><span class="token punctuation">,</span>
                <span class="token punctuation">}</span><span class="token punctuation">,</span>
                <span class="token punctuation">{</span>
                        Scheme<span class="token punctuation">:</span> <span class="token string">"http"</span><span class="token punctuation">,</span>
                        Host<span class="token punctuation">:</span>   <span class="token string">"localhost:9092"</span><span class="token punctuation">,</span>
                <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
        log<span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":9090"</span><span class="token punctuation">,</span> proxy<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>NOTE：这段代码来自 <a href="http://blog.charmes.net/2015/07/reverse-proxy-in-go.html。" target="_blank" rel="noopener">http://blog.charmes.net/2015/07/reverse-proxy-in-go.html。</a></p>
<p>我们让代理监听在 <code>9090</code> 端口，在后端启动两个返回不同响应的服务器分别监听在 <code>9091</code> 和 <code>9092</code> 端口，通过 <code>curl</code> 访问，可以看到多次请求会返回不同的结果。</p>
<pre class=" language-bash"><code class="language-bash">➜  curl http://127.0.0.1:9090
116064a9eb83
➜  curl http://127.0.0.1:9090
8f7ccc11718f
</code></pre>
<p>同样的，这段代码也只是一个 demo，存在着很多问题，比如没有错误处理机制，如果后端某个服务器挂了，代理会返回 502 错误，更好的做法是把请求转发到另外的可用服务器。当然也可以添加更多的特性让它更好用，比如动态地添加后端服务器列表；根据后端服务器的负载情况进行负载转发等等。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="http://blog.charmes.net/2015/07/reverse-proxy-in-go.html" target="_blank" rel="noopener">Reverse Proxy in Go</a></li>
<li><a href="http://www.darul.io/post/2015-07-22_go-lang-simple-reverse-proxy" target="_blank" rel="noopener">Go lang simple reverse proxy</a></li>
<li><a href="http://shop.oreilly.com/product/9781565925090.do" target="_blank" rel="noopener">http 权威指南</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="HTTP 隧道代理原理和实现" href="/2017/03/22/http-tunnel-proxy-and-golang-implementation/">
        ← HTTP 隧道代理原理和实现
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="容器生态系统简介" href="/2017/03/15/container-ecosystem/">
        容器生态系统简介 →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#正向代理和反向代理"><span class="toc-text">正向代理和反向代理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#代理服务器怎么知道目的服务器的地址？"><span class="toc-text">代理服务器怎么知道目的服务器的地址？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#代理路径"><span class="toc-text">代理路径</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#代理服务器实现"><span class="toc-text">代理服务器实现</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#正向代理"><span class="toc-text">正向代理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#反向代理"><span class="toc-text">反向代理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">HTTP 代理原理和实现</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2017/03/21/http-proxy-and-golang-implementation/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2017/03/21/http-proxy-and-golang-implementation/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
